#include "TargetListener.h"
#include "Bird.h"
#include "Level.h"

TargetListener::TargetListener( Root *root,Camera *camera, SceneManager* sm, RenderWindow *rw,OIS::Mouse* mouse,OIS::Keyboard*kb,OIS::InputManager* im, Ogre::SceneNode* catspultNode, OgreNewt::World* world ) :mMouse(mouse),mRoot(root),mCamera(camera),mSceneMgr(sm),mWindow(rw),mKeyboard(kb),mInputManager(im),catspultNd(catspultNode),mWorld(world)
{
	mShutDown = false;
	sideView = false;

	omega = 0.1f;
	speed = 3.0f;
	timer = 0.0f;

	cameraNode = catspultNd->createChildSceneNode();
	cameraNode->attachObject(mCamera);
	cameraNode->translate(0.0f, 3.0f, 15.0f);

	mDragging = false;
	mDragDist = 0.0f;
	mPickBodyController = NULL;

	// position camera
	Ogre::Vector3 start(mCamera->getPosition()+Ogre::Vector3(0.0f, 1000.0f, 0.0f));
	Ogre::Vector3 end(mCamera->getPosition()+Ogre::Vector3(0.0f, -1000.0f, 0.0f));
	OgreNewt::BasicRaycast castRay (mWorld, start, end, true);
	OgreNewt::BasicRaycast::BasicRaycastInfo info = castRay.getFirstHit();

	mCamera->setPosition(0.0, 3.0f + start.y + (end.y - start.y) * info.mDistance, 15.0);

	mAnimationState = NULL;

	mDragging = false;
	mDragDist = 0.0f;
	mPickBodyController = NULL;

	windowResized(mWindow);

	bird = Level::getInstance()->giveMeOneBird();
	body = bird->body;
	node = bird->sceneNode;
	body->setPositionOrientation(Vector3(0.0, 0.0, 375.0), mCamera->getDerivedOrientation());

	Ogre::Entity* spring1 = mSceneMgr->createEntity("spring1", "pcy1.mesh");
	springNode1 = catspultNd->createChildSceneNode();
	springNode1->attachObject(spring1);
	springNode1->translate(1.5, 0.0, 0.0);
	springNode1->setAutoTracking(true, node, Vector3::NEGATIVE_UNIT_X);

	Ogre::Entity* spring2 = mSceneMgr->createEntity("spring2", "pcy2.mesh");
	springNode2 = catspultNd->createChildSceneNode();
	springNode2->attachObject(spring2);
	springNode2->translate(-1.5, 0.0, 0.0);
	springNode2->setAutoTracking(true, node, Vector3::UNIT_X);
}

TargetListener::~TargetListener(void)
{
}

bool TargetListener::frameRenderingQueued(const Ogre::FrameEvent& evt) {
	if(mWindow->isClosed())
		return false;

	if(mShutDown)
		return false;

	Vector3 trans, strafe, vec;
	Quaternion quat = mCamera->getDerivedOrientation();

	vec = Vector3(0.0,0.0,-0.5);
	trans = quat * vec;

	vec = Vector3(0.5,0.0,0.0);
	strafe = quat * vec;

	if (mKeyboard->isKeyDown(OIS::KC_W))
		mCamera->moveRelative(trans);

	if (mKeyboard->isKeyDown(OIS::KC_S))
		mCamera->moveRelative(trans * -1.0);

	if (mKeyboard->isKeyDown(OIS::KC_A))
		mCamera->moveRelative(strafe * -1.0);

	if (mKeyboard->isKeyDown(OIS::KC_D))
		mCamera->moveRelative(strafe);

	//------------------------------------------------------------------------------------
	if (mKeyboard->isKeyDown(OIS::KC_SPACE))
	{
		if (timer <= 0.0)
		{
			Vector3 vec = Vector3(0,0,-1);
			Quaternion shootorient = vec.getRotationTo(catspultNd->getPosition()-node->getPosition());
			Vector3 pos = node->getPosition();
			Vector3 dir = shootorient * vec;
			
			Real dis = catspultNd->getPosition().squaredDistance(pos);
				
			if (bird) {
				bird->birdState = Bird::BirdStateType::FLYING;

				body->setVelocity((dir * dis));
				body->setPositionOrientation(pos,shootorient);
				body->setStandardForceCallback();
				bird->fly();

				mAnimationState = bird->entity->getAnimationState("dou");
				mAnimationState->setLoop(true);
				mAnimationState->setEnabled(true);

				bird = Level::getInstance()->giveMeOneBird();
				if (bird) {
					node = bird->sceneNode;
					body = bird->body;
					body->setPositionOrientation(Vector3(6.0, 0.0, 385.0), shootorient);

					springNode1->setAutoTracking(true, node, Vector3::NEGATIVE_UNIT_X);
					springNode2->setAutoTracking(true, node, Vector3::UNIT_X);

					timer = 0.2;
				} else {
					springNode1->setAutoTracking(true, catspultNd, Vector3::NEGATIVE_UNIT_X);
					springNode2->setAutoTracking(true, catspultNd, Vector3::UNIT_X);

					Vector3 springPos1 = springNode1->getPosition();
					Vector3 springScale1 = springNode1->getScale();
					Vector3 springPos2 = springNode2->getPosition();
					Vector3 springScale2 = springNode2->getScale();

					Ogre::Real absX1 = abs(springPos1.x);
					springNode1->scale(absX1/springScale1.x, 1.0, 1.0);

					Ogre::Real absX2 = abs(springPos2.x);
					springNode2->scale(absX2/springScale2.x, 1.0, 1.0);
				}
			}
		}
	}

	if (mKeyboard->isKeyDown(OIS::KC_B)) {
		if (timer <= 0) {
			Vector3 vec = Vector3(0,0,-1);
			Quaternion shootorient = mCamera->getDerivedOrientation();
			Vector3 pos = mCamera->getDerivedPosition();
			Vector3 dir = shootorient * vec;

			if (bird) {
				bird->birdState = Bird::BirdStateType::FLYING;

				body->setVelocity((dir * 700));
				body->setPositionOrientation(pos,shootorient);
				body->setStandardForceCallback();
				bird->fly();

				mAnimationState = bird->entity->getAnimationState("dou");
				mAnimationState->setLoop(true);
				mAnimationState->setEnabled(true);

				bird = Level::getInstance()->giveMeOneBird();

				timer = 0.2;
			}
		}
	}

	if (bird && springNode1->getAutoTrackTarget()==node) {
		Vector3 springPos1 = springNode1->getPosition();
		Vector3 springScale1 = springNode1->getScale();
		Vector3 springPos2 = springNode2->getPosition();
		Vector3 springScale2 = springNode2->getScale();
		Vector3 nodePos = node->getPosition();

		Ogre::Real absX1 = abs(nodePos.x - springPos1.x);
		springNode1->scale(absX1/springScale1.x, 1.0, 1.0);

		Ogre::Real absX2 = abs(nodePos.x - springPos2.x);
		springNode2->scale(absX2/springScale2.x, 1.0, 1.0);
	}

	if (mAnimationState) {
		mAnimationState->addTime(evt.timeSinceLastFrame);
	}
	
	timer -= evt.timeSinceLastFrame;

	if (!mousePick ()) {
//		Real timestep = evt.timeSinceLastFrame;
// 		static Real pitch (0.0);
// 		static Real yaw (0.0);
// 		if (mMouse->getMouseState().buttonDown(OIS::MB_Left)) {
// 
// 			Real rate = (30.0f * 3.1416f/ 180.0f) * timestep;
// 
// 			int yawDir = mMouse->getMouseState().X.rel;
// 			yaw += yawDir * rate;
// 
// 			int pitchDir = mMouse->getMouseState().Y.rel;
// 			pitch += pitchDir * rate;
// 			if (pitch >  80.0f * 3.1416f/ 180.0f) pitch =  80.0f * 3.1416f/ 180.0f;
// 			if (pitch < -80.0f * 3.1416f/ 180.0f) pitch = -80.0f * 3.1416f/ 180.0f;
// 
// 			Matrix3 matrix;
// 			matrix.FromEulerAnglesZYX (Radian (0.0f), Radian (yaw), Radian (pitch));
// 			mCamera->setOrientation (Quaternion (matrix));
//		}
	}

	//Need to capture/update each device
	mKeyboard->capture();
	mMouse->capture();

	return true;
}

bool TargetListener::keyPressed( const OIS::KeyEvent &arg ) {
	Vector3 v;
	switch (arg.key) {
	case OIS::KC_F5 :
		Ogre::TextureManager::getSingleton().reloadAll();
		break;
	case OIS::KC_SYSRQ :
		mWindow->writeContentsToTimestampedFile("screenshot", ".jpg");
		break;
	case OIS::KC_ESCAPE :
		mShutDown = true;
		break;
	default:
		break;
	}

	return true;
}

bool TargetListener::mouseMoved( const OIS::MouseEvent &arg ) {
	if (mPickBodyController) {
		//catspultNd->yaw(Degree(-arg.state.X.rel * 0.25f));
	}
// 	mCamera->yaw(Degree(-arg.state.X.rel * 0.25f));
// 	mCamera->pitch(Degree(-arg.state.Y.rel * 0.25f));
	return true;
}

//Adjust mouse clipping area
void TargetListener::windowResized(Ogre::RenderWindow* rw) {
	unsigned int width, height, depth;
	int left, top;
	rw->getMetrics(width, height, depth, left, top);

	const OIS::MouseState &ms = mMouse->getMouseState();
	ms.width = width;
	ms.height = height;
}

void TargetListener::windowClosed(Ogre::RenderWindow* rw) {

	//Only close for window that created OIS (the main window in these demos)
	if( rw == mWindow ) {
		if( mInputManager ) {
			mInputManager->destroyInputObject( mMouse );
			mInputManager->destroyInputObject( mKeyboard );

			OIS::InputManager::destroyInputSystem(mInputManager);
			mInputManager = 0;
		}
	}
}

bool TargetListener::mousePick() {
	int mouseKey1;
	Ogre::Real rayDist = 100.0f;
	Ogre::Real MAX_FRICTION_ANGULAR_GRAVITY	= 15.0f;
	Ogre::Real MAX_FRICTION_LINEAR_ACCELERATION = 100.0f;

	mouseKey1 = mMouse->getMouseState().buttonDown(OIS::MB_Left);
	if (mouseKey1) {
		if (!mDragging) {
			// perform a raycast!
			// start at the camera, and go for 100 units in the Z direction.
			Ogre::Vector3 start, end;

			Ogre::Real mx,my;
			mx = Ogre::Real (mMouse->getMouseState().X.abs) / Ogre::Real(mMouse->getMouseState().width);
			my = Ogre::Real (mMouse->getMouseState().Y.abs) / Ogre::Real(mMouse->getMouseState().height);
			Ogre::Ray camray = mCamera->getCameraToViewportRay(mx,my);

			start = camray.getOrigin();
			end = camray.getPoint (rayDist);

			OgreNewt::BasicRaycast* ray = new OgreNewt::BasicRaycast( mWorld, start, end, true );
			OgreNewt::BasicRaycast::BasicRaycastInfo info = ray->getFirstHit();

			OgreNewt::Body* pickedBody = NULL;
			if (info.mBody) {
				Ogre::Real mass;
				Ogre::Vector3 inertia;
				info.mBody->getMassMatrix(mass, inertia );
				if (mass > 0.0f) {
					mDragDist = info.mDistance;
					pickedBody = info.mBody;
				}
			} 

			if (pickedBody) {
				// info.mDistance is in the range [0,1].
				Ogre::Vector3 globalpt = camray.getPoint (100.0 * mDragDist);

				// attach a kinematic controller to the body
				mPickBodyController = new OgreNewt::KinematicController (pickedBody, globalpt);

				// set the pick mode to fix joint
				mPickBodyController->setPickingMode (1);

				// set the maximum angular friction power
				//mPickBodyController->setMaxAngularFriction (10.0f);
				mPickBodyController->setMaxAngularFriction(MAX_FRICTION_ANGULAR_GRAVITY);

				// set the maximum linear friction power
				mPickBodyController->setMaxLinearFriction(MAX_FRICTION_LINEAR_ACCELERATION);
			}
		}


		if (mPickBodyController) {
			// init pick mode
			Ogre::Real mx,my;
			mx = Ogre::Real (mMouse->getMouseState().X.abs) / Ogre::Real(mMouse->getMouseState().width);
			my = Ogre::Real (mMouse->getMouseState().Y.abs) / Ogre::Real(mMouse->getMouseState().height);
			Ogre::Ray camray = mCamera->getCameraToViewportRay(mx,my);

			Ogre::Vector3 start(camray.getOrigin());
			Ogre::Vector3 end (camray.getPoint (rayDist));
			Ogre::Vector3 point (start + (end - start) * mDragDist);

			mPickBodyController->setTargetPosit (point);
		}
	} else {
		if (mPickBodyController) {
			delete mPickBodyController;
		}
		mPickBodyController = NULL;
	}

	mDragging = mouseKey1 ? true : false;

	return mPickBodyController ? true : false;
}

bool TargetListener::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	if (id==OIS::MB_Right) {
		sideView = true;
		mCamera->moveRelative(Vector3(25.0f, 0.0f, -25.0f));
		mCamera->yaw(Degree(90.0));
	}
	return true;
}

bool TargetListener::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	if (id==OIS::MB_Right) {
		sideView = false;
	}
	return true;
}
